﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2011-11-17 15:50:52Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Outcoder.Reflection
{
	public static class PropertyUtility
	{
		public static Func<TProperty> CreateGetter<TProperty>(
			this PropertyInfo propertyInfo, object owner)
		{
			ArgumentValidator.AssertNotNull(propertyInfo, "propertyInfo");
			ArgumentValidator.AssertNotNull(owner, "owner");

			Type getterType = Expression.GetFuncType(
								new[] { propertyInfo.PropertyType });
			//Type getterType = typeof(Func<>).MakeGenericType(propertyInfo.PropertyType);

#if NETFX_CORE
			MethodInfo methodInfo = propertyInfo.GetMethod;
			object getter = methodInfo.CreateDelegate(
					getterType, owner);
#else
			object getter = Delegate.CreateDelegate(
								getterType, owner, propertyInfo.GetGetMethod());
#endif
			return (Func<TProperty>)getter;
		}

		public static TDelegate CreateDelegate<TDelegate>(
			this PropertyInfo propertyInfo,
			object owner)
		{
			ArgumentValidator.AssertNotNull(propertyInfo, "propertyInfo");
			ArgumentValidator.AssertNotNull(owner, "owner");

#if NETFX_CORE
			MethodInfo getMethodInfo = propertyInfo.GetMethod;
			object getter = getMethodInfo.CreateDelegate(
					typeof(TDelegate), owner);
#else
			object getter = Delegate.CreateDelegate(
								typeof(TDelegate), owner, propertyInfo.GetGetMethod());
#endif
			return (TDelegate)getter;
		}

		public static Action<TProperty> CreateSetter<TProperty>(
			this PropertyInfo propertyInfo, object owner)
		{
			ArgumentValidator.AssertNotNull(propertyInfo, "propertyInfo");
			ArgumentValidator.AssertNotNull(owner, "owner");

			var propertyType = propertyInfo.PropertyType;
			var setterType = Expression.GetActionType(new[] { propertyType });

#if NETFX_CORE
			MethodInfo setMethodInfo = propertyInfo.SetMethod;
			Delegate setter = setMethodInfo.CreateDelegate(setterType, owner);
#else
			Delegate setter = Delegate.CreateDelegate(
						setterType, owner, propertyInfo.GetSetMethod());
#endif
			return (Action<TProperty>)setter;
		}

		public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T>> expression)
		{
			var memberExpression = expression.Body as MemberExpression;
			if (memberExpression == null)
			{
				throw new ArgumentException(
					"MemberExpression expected.", "expression");
			}

			if (memberExpression.Member == null)
			{
				throw new ArgumentException("Member should not be null.");
			}

#if NETFX_CORE
			if (!(memberExpression.Member is PropertyInfo))
			{
				throw new ArgumentException("Property expected.", "expression");
			}
#else
			if (memberExpression.Member.MemberType != MemberTypes.Property)
			{
				throw new ArgumentException("Property expected.", "expression");
			}
#endif
			PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;
			return propertyInfo;
		}
	}
}
